import { ElMessage } from "element-plus"
import { Connection, Endpoint, jsPlumbInstance } from "jsplumb"
import { DefineComponent } from "vue"
import { bindConnEvents } from "./events"
import { addEndpointsToNode, addMidpoint } from "./io"
import { Edge, Midpoint, Node, Port } from "./models"
import { parseEndpointID, parseMidpointUUID, portToEndpointUUID, toMidpointUUID, toPortUUID } from "./utils"

// 真正需要生成UUID的是节点和边。
// 只要确保所有节点或者边的UUID不重复即可。
// 因此这个是子类，需要从StatusManager里面查询。
class UUIDGenerator {
    _current_value = 1
    generatedIDs = new Set<string>() // 目前删除节点可能有问题，因此需要用这样的方式防止在一个dom上生成重复的id。
    generate(nodeType: string): string {
        for (let i = 1; i < 114514; i++) {
            const newID = nodeType + i.toString()
            if (document.getElementById(newID) == null && !this.generatedIDs.has(newID)) {
                this.generatedIDs.add(newID)
                return newID
            }
        }
        throw Error("无法生成新的ID")
    }
}
const generator = new UUIDGenerator()

export class StatusManager {
    _vue: { items: Node[] } = {} as any
    plumb: jsPlumbInstance | null = null
    edges: Edge[] = []
    midpoints: Midpoint[] = []
    revalidateQueue: string[] = []
    timer = -1
    startAutoUpdate() {
        this.timer = window.setInterval(() => {
            if (this.plumb != null) {
                this.revalidate()
            }
        }, 20)
    }
    stopAutoUpdate() {
        window.clearInterval(this.timer)
    }
    // 以某种类型的节点为基础，生成新的UUID
    newUUID(nodeType: string) {
        return generator.generate(nodeType)
    }
    newEdgeUUID(): string {
        const edgeUUIDs = new Set()
        for (const edge of this.edges) {
            edgeUUIDs.add(edge.id)
        }
        for (let i = 1; i < 114514; i++) {
            const newEdgeID = 'edge' + i.toString()
            if (!edgeUUIDs.has(newEdgeID)) {
                return newEdgeID
            }
        }
        throw Error('Cannot generate the id of edge!')
    }

    setPlumb(vue: { items: Node[] }, plumb: jsPlumbInstance) {
        this.plumb = plumb
        this._vue = vue
    }
    checkDuplicatedEdge() {
        const edgesSet: Set<string> = new Set()
        for (const i in this.edges) {
            if (!edgesSet.has(this.edges[i].id)) {
                edgesSet.add(this.edges[i].id)
            } else {
                console.error(this.edges[i].id)
                throw Error(`Edge id ${this.edges[i].id} is duplicated!`)
            }
        }
    }
    addEdge(edge: Edge) {
        this.edges.push(edge)
        if (edge.srcEndpointID.includes('-') || edge.destEndpointID.includes('-')) {
            console.error("Invalid endpoint id for edge.", edge)
            throw Error(`Invalid endpoint id for edge.`)
        }
        this.checkDuplicatedEdge()
    }

    getEdge(edgeID: string): Edge {
        const edge = this.edges.find((edge) => edge.id == edgeID)
        if (edge == null) {
            console.error(edgeID, this.edges)
            throw Error("edge is null")
        }
        return edge
    }

    delEdge(edgeID: string): void {
        const edgeIndex = this.edges.findIndex((edge) => edge.id == edgeID)
        this.edges.splice(edgeIndex, 1)
    }

    getAllConnectionsOnEdge(edgeID: string): Connection[] {
        const conns: Connection[] = []
        if (this.plumb != null) {
            this.plumb.getAllConnections().forEach((conn: Connection) => {
                if (this.queryEdgeofConnection(conn).id === edgeID) {
                    conns.push(conn)
                }
            });
            return conns
        } else {
            throw Error('jsplumb is null!')
        }

    }
    // getAllConnectionsFromEndpoint(endpointID: string): Connection[] {
    //     if (this.plumb == null) {
    //         throw Error("Plumb is null!")
    //     }

    //     this.getEdgeByEndpoints()
    //     this.getAllConnectionsOnEdge
    //     console.log(endpointID, this.plumb.select({ source: endpointID }), this.plumb.select({ target: endpointID }))
    //     return []
    // }
    getRelatedEdges(nodeID: string, prop: 'src' | 'dest' | 'all'): Edge[] {
        const src: Edge[] = []
        const dest: Edge[] = []
        this.edges.forEach((edge: Edge) => {
            if (edge.srcNode === nodeID) {
                src.push(edge)
            }
        })
        this.edges.forEach((edge: Edge) => {
            if (edge.destNode === nodeID) {
                dest.push(edge)
            }
        })
        if (prop === 'src') {
            return src
        } else if (prop == 'dest') {
            return dest
        } else {
            return src.concat(dest)
        }
    }
    getEndpointsUUIDOfConnection(conn: Connection): { srcUUID: string, destUUID: string } {

        return {
            srcUUID: this.getEndpointUUID(conn.endpoints[0]),
            destUUID: this.getEndpointUUID(conn.endpoints[1])
        }

    }

    getConnectionsOnEndpoint(endpointID: string): Connection[] {
        if (this.plumb == null) {
            throw Error("Plumb is null!")
        }
        const connections: Connection[] = []

        this.plumb.getAllConnections().forEach((conn: Connection) => {

            if (
                (this.getEndpointUUID(conn.endpoints[0]) === endpointID) ||
                (this.getEndpointUUID(conn.endpoints[1]) === endpointID)
            ) {
                connections.push(conn)
            }
        })
        return connections
    }

    getEdgeByEndpoints(srcEndpointID: string, destEndpointID: string): Edge {
        const destEndpointRelative = parseEndpointID(destEndpointID)
        const srcEndpointRelative = parseEndpointID(srcEndpointID)
        const ret = this.edges.find((edge: Edge) => ((edge.destEndpointID === destEndpointRelative.endpointID)
            && (edge.srcEndpointID === srcEndpointRelative.endpointID) &&
            (edge.srcNode === srcEndpointRelative.nodeID) &&
            (edge.destNode === destEndpointRelative.nodeID)
        ))
        if (ret == null) {
            console.error(this.edges, "src:", srcEndpointRelative, "dest:", destEndpointRelative)
            throw Error(`Edge with src-endpoint ${srcEndpointID}, dest-endpoint ${destEndpointID} not found`)
        }
        return ret
    }

    sortMidpoints(midpoints: Midpoint[]) {
        midpoints.sort((a: Midpoint, b: Midpoint) => a.index - b.index)
    }

    // 获取和边相关的所有Midpoint
    getRelatedMidpoints(edgeID: string): Midpoint[] {
        const midPoints = []
        for (let i = 0; i < this.midpoints.length; i++) {
            if (this.midpoints[i].edgeID === edgeID) {
                midPoints.push(this.midpoints[i])
            }
        }
        this.sortMidpoints(midPoints)
        return midPoints
    }
    getEndpointUUID(endpoint: Endpoint): string {
        return (endpoint as any).getUuid()
    }

    getEndpointPosition(endpointUUID: string): { top: number; left: number } {
        const endpoints = (this.plumb as any).selectEndpoints()
        let value: { top: number; left: number } | null = null
        endpoints.each((item: any) => {
            if (item.getUuid() === endpointUUID) {

                value = { left: parseInt(item.canvas.style.left.split("px")[0]), top: parseInt(item.canvas.style.top.split('px')[0]) }
            }
        })
        if (value == null) {
            throw Error(`endpoint ${endpointUUID} not found!`)
        } else {
            return value
        }
    }

    // 计算中继节点的约束是否满足
    calculateMidpointsPositionWithConstraint(midpointUUID: string, newLeft: number, newTop: number,): { midpointUUID: string, top: number, left: number }[] {
        const l = midpointUUID.split('-')
        const edge = this.getEdge(l[1])
        const midpointIndex = parseInt(l[2])
        const srcNodeFlipped = this.getNode(edge.srcNode).flip
        const destNodeFlipped = this.getNode(edge.destNode).flip
        console.log(srcNodeFlipped, destNodeFlipped)
        const srcPoint = this.getEndpointPosition(edge.srcNode + '-output-' + edge.srcEndpointID)
        const destPoint = this.getEndpointPosition(edge.destNode + '-input-' + edge.destEndpointID)
        const relatedMidpoints = this.getRelatedMidpoints(edge.id)
        if (!(relatedMidpoints.length > 0 && relatedMidpoints.length % 2 === 0)) {
            throw Error("Midpoint 应当是偶数个，而且大于0！")
        }
        const deltaXAbs = 10 // 中继节点与端口距离的最小值
        if (midpointIndex === 0) {
            // 对于第一个midpoint，最靠近前驱节点的output
            // 如果是第一个midpoint，横向位置必须保持水平。
            const midpoint1 = relatedMidpoints[1] // 第二个midpoint，横向位置与第一个相同，并且高度应当与输出端口对齐
            const midpoint1UUID = toMidpointUUID(midpoint1.edgeID, midpoint1.index)
            const midpoint1Pos = this.getMidpointPos(midpoint1UUID)

            if (!srcNodeFlipped && newLeft < srcPoint.left + deltaXAbs) {
                newLeft = srcPoint.left + deltaXAbs
            } else if (srcNodeFlipped && newLeft > srcPoint.left - deltaXAbs) {
                newLeft = srcPoint.left - deltaXAbs
            }
            return [{ midpointUUID: midpointUUID, top: srcPoint.top, left: newLeft },
            { midpointUUID: midpoint1UUID, top: midpoint1Pos.top, left: newLeft }]

        } else if (midpointIndex === relatedMidpoints.length - 1) {
            // 对于最靠近下一节点输入端口的midpoint
            const midpointLast2 = relatedMidpoints[midpointIndex - 1] // 倒数第二个midpoint，横向位置与倒数第一个相同，高度与输入端口对齐
            const midpointLast2UUID = toMidpointUUID(midpointLast2.edgeID, midpointLast2.index)
            const midpointLast2Pos = this.getMidpointPos(midpointLast2UUID)
            if (!destNodeFlipped && newLeft > destPoint.left - deltaXAbs) {
                // 如果目标节点未被反转，则横坐标为目标节点的横坐标减去一个值
                newLeft = destPoint.left - deltaXAbs
            } else if (destNodeFlipped && newLeft < destPoint.left + deltaXAbs) {
                // 如果目标节点被反转，则横坐标为目标节点的横坐标加上一个值
                newLeft = destPoint.left + deltaXAbs
            }
            return [{ midpointUUID: midpointUUID, top: destPoint.top, left: newLeft },
            { midpointUUID: midpointLast2UUID, top: midpointLast2Pos.top, left: newLeft }]
        } else {
            // 对于其他的midpoint。此时relatedMidpoints的长度必然为4及以上。
            const midpointPos = this.getMidpointPos(midpointUUID)
            const nextMidpoint = relatedMidpoints[midpointIndex + 1]
            const nextMidpointUUID = toMidpointUUID(nextMidpoint.edgeID, nextMidpoint.index)
            const nextMidpointPos = this.getMidpointPos(nextMidpointUUID)

            const prevMidpoint = relatedMidpoints[midpointIndex - 1]
            const prevMidpointUUID = toMidpointUUID(prevMidpoint.edgeID, prevMidpoint.index)
            const prevMidpointPos = this.getMidpointPos(prevMidpointUUID)
            let vAlignedMidpointPos: { left: number, top: number } | null = null // x坐标相同的Midpoint
            let hAlignedMidpointPos: { left: number, top: number } | null = null
            let vAlignedMidpointUUID = ''
            let hAlignedMidpointUUID = ''

            // if (Math.abs(nextMidpointPos.left - midpointPos.left) <= 5) {
            if (midpointIndex % 2 === 0) {
                // 对于非首尾的中继节点，当index为奇数，则和前驱节点垂直对齐，和后继水平对齐。
                // 反之则与前驱节点水平对齐，与后继节点垂直对齐。
                vAlignedMidpointPos = nextMidpointPos
                vAlignedMidpointUUID = nextMidpointUUID
                hAlignedMidpointPos = prevMidpointPos
                hAlignedMidpointUUID = prevMidpointUUID
            } else {
                vAlignedMidpointPos = prevMidpointPos
                vAlignedMidpointUUID = prevMidpointUUID
                hAlignedMidpointPos = nextMidpointPos
                hAlignedMidpointUUID = nextMidpointUUID
            }
            return [{ midpointUUID: midpointUUID, top: newTop, left: newLeft },
            { midpointUUID: hAlignedMidpointUUID, top: newTop, left: hAlignedMidpointPos.left },
            { midpointUUID: vAlignedMidpointUUID, top: vAlignedMidpointPos.top, left: newLeft }
            ]
        }
    }
    addMidpoint(midpoint: Midpoint) {
        this.midpoints.push(midpoint)
    }
    removeMidpoint(edgeID: string, index: number) {
        const midpointIndex = this.midpoints.findIndex((midpoint: Midpoint) => midpoint.index == index && midpoint.edgeID == edgeID)
        this.midpoints.splice(midpointIndex, 1)
    }
    reAllocateMidpointIndex(edgeID: string) {
        const midpoints: Midpoint[] = this.getRelatedMidpoints(edgeID)
        this.sortMidpoints(midpoints)
        for (let i = 0; i < midpoints.length; i++) {
            midpoints[i].index = i
        }
    }

    setMidpointPos(midpointUUID: string, left: number, top: number) {
        this.setMidpointPosLazy(midpointUUID, left, top)
        this.revalidate()

    }
    revalidate() {
        if (this.plumb == null) {
            throw null
        }
        for (const i in Array.from(new Set(this.revalidateQueue))) {
            this.plumb.revalidate(this.revalidateQueue[i]);
        }
        this.revalidateQueue = []
    }
    setMidpointPosLazy(midpointUUID: string, left: number, top: number) {
        const elem: HTMLDivElement = document.getElementById(
            midpointUUID
        ) as HTMLDivElement;
        if (elem == null) {
            throw Error(`Midpoint with UUID ${midpointUUID} does not exist on dom!`)
        }
        elem.style.top = top + "px";
        elem.style.left = left + "px";
        this.revalidateQueue.push(midpointUUID)
    }
    getMidpointPos(midpointUUID: string): { left: number, top: number } {
        const elem: HTMLDivElement | null = document.getElementById(
            midpointUUID
        ) as HTMLDivElement | null;
        if (elem == null) {
            console.error("Midpoint element null", midpointUUID,)
            throw Error(`element is null for midpoint uuid: ${midpointUUID}`)
        }
        return {
            left: parseInt(elem.style.left.split("px")[0]),
            top: parseInt(elem.style.top.split("px")[0])
        }
    }
    // 查询midpoint
    queryMidpoint(midpointUUID: string): Midpoint {
        const { index, edgeID } = parseMidpointUUID(midpointUUID)
        const ret = this.midpoints.find((midpoint: Midpoint) => (edgeID == midpoint.edgeID) && (index == midpoint.index))
        if (ret == null) {
            console.error(this.midpoints, midpointUUID)
            throw Error(`Midpoint not found`)
        }
        return ret
    }
    // 通过connection来查询所属edge的ID。
    queryEdgeofConnection(conn: Connection): Edge {
        const startElemID: string = (conn.endpoints[0] as any).elementId; // 如果前驱节点是奇数号或者端口，则其为横向；。
        const endElemID: string = (conn.endpoints[1] as any).elementId;

        if ((!startElemID.startsWith("midpoint")) && (!endElemID.startsWith("midpoint"))) {
            // 如果起始于endpoint，结束于endpoint
            const startEndpoint = parseEndpointID(this.getEndpointUUID(conn.endpoints[0]))
            const endEndpoint = parseEndpointID(this.getEndpointUUID(conn.endpoints[1]))
            const edge = this.getEdgeByEndpoints(portToEndpointUUID(startEndpoint), portToEndpointUUID(endEndpoint))
            return edge
        } else if ((startElemID.startsWith("midpoint"))) {
            // 起始于Midpoint
            return this.getEdge(this.queryMidpoint(startElemID).edgeID)

        } else {
            // 结束于Midpoint
            return this.getEdge(this.queryMidpoint(endElemID).edgeID)
        }
    }
    // 根据约束条件，重新求解边中所有Midpoint的约束，并且重新绘制。
    fixEdgeConstraints(edgeID: string) {
        const relatedMidpoints = this.getRelatedMidpoints(edgeID)
        for (const i in relatedMidpoints) {
            const mp = relatedMidpoints[i]
            const midpointUUID = toMidpointUUID(mp.edgeID, mp.index)
            const mpPos = this.getMidpointPos(midpointUUID)
            const newPositions = this.calculateMidpointsPositionWithConstraint(midpointUUID, mpPos.left, mpPos.top)
            for (const j in newPositions) {
                this.setMidpointPos(newPositions[j].midpointUUID, newPositions[j].left, newPositions[j].top)
            }
        }
    }


    async redrawEdge(vue: any, edge: Edge, relatedMidpoints: Midpoint[]) {
        if (this.plumb == null) {
            throw Error("Plumb is null!")
        }
        const uuidSrc = toPortUUID(edge.srcNode, "output", edge.srcEndpointID);
        const uuidDest = toPortUUID(edge.destNode, "input", edge.destEndpointID);
        const UUIDsToConnect: string[][] = [];
        let lastUUID = uuidSrc;
        for (let i = 0; i < relatedMidpoints.length; i++) {
            const midpoint = relatedMidpoints[i];
            if (midpoint.index != i) {
                console.error(relatedMidpoints);
                throw Error("Midpoint index not match");
            }
            const midpointUUID =
                "midpoint-" + midpoint.edgeID + "-" + midpoint.index;

            this.addMidpoint(midpoint);

            await vue.$nextTick()
            this.setMidpointPos(
                midpointUUID,
                midpoint.left,
                midpoint.top
            );

            addMidpoint(this.plumb, this, midpointUUID);
            UUIDsToConnect.push([lastUUID, midpointUUID + "-input"]);
            lastUUID = midpointUUID + "-output";
            this.plumb.recalculateOffsets(midpointUUID)
        }
        UUIDsToConnect.push([lastUUID, uuidDest]);

        for (let i = 0; i < UUIDsToConnect.length; i++) {
            const conn = this.plumb!.connect({
                uuids: [UUIDsToConnect[i][0], UUIDsToConnect[i][1]],
            });
            bindConnEvents(conn)


        }
    }

    // 重新绘制边。这是因为UUID是写死的，只能删去UUID不符的组件，然后重画所有内容。
    async resetEdge(edgeID: string, vue: any): Promise<string> {
        if (this.plumb == null) {
            throw Error("Plumb is null!")
        }
        const newEdgeID = edgeID + "_new"

        const allConnections = this.getAllConnectionsOnEdge(edgeID);
        const relatedMidpoints = this.getRelatedMidpoints(edgeID)

        for (const i in allConnections) {
            this.plumb.deleteConnection(allConnections[i])
        }
        const oldElemIDs: string[] = []
        for (const i in relatedMidpoints) {
            const midpointUUID = toMidpointUUID(edgeID, relatedMidpoints[i].index)
            this.removeMidpoint(edgeID, relatedMidpoints[i].index);

            relatedMidpoints[i].edgeID = newEdgeID
            oldElemIDs.push(midpointUUID)
        }
        const oldEdge = this.getEdge(edgeID)
        oldEdge.id = newEdgeID

        const edge = this.getEdge(newEdgeID)
        await vue.$nextTick()
        await this.redrawEdge(vue, edge, relatedMidpoints)
        return newEdgeID

    }
    // 为一条边添加新的endpoint
    // 注意，应当同时添加两个endpoint!
    // 如果previousIndex>=0，说明是从midpoint发出，否则是在输出端点发出。
    bend(edgeID: string, edgePos: 'horizontal' | 'vertical', previousIndex: number, previousPos: { left: number, top: number }, evtPos: { left: number, top: number }, vue: DefineComponent, targetConnection: Connection): void {
        const relatedEndpoints = this.getRelatedMidpoints(edgeID)
        const srcEndpointUUID = this.getEndpointUUID(targetConnection.endpoints[0]) // 要被删除的边的起始endpoint
        const destEndpointUUID = this.getEndpointUUID(targetConnection.endpoints[1]) // 要被删除的边的终止endpoint
        const newMidpointIndex1 = previousIndex + 1
        const newMidpointIndex2 = previousIndex + 2
        if (previousIndex == -1) {
            for (const i in relatedEndpoints) {
                relatedEndpoints[i].index += 2
            }
        } else {
            if (!((previousIndex >= 0) && (previousIndex <= relatedEndpoints.length - 1))) {
                console.error(previousIndex, relatedEndpoints)
                throw Error(`Invalid previous index ${previousIndex}. `)
            }
            for (let i = 0; i < relatedEndpoints.length; i++) {
                if (i > previousIndex) {
                    relatedEndpoints[i].index += 2
                }
            }
        }
        if (edgePos == 'horizontal') {
            // 如果边是水平的，则添加两个垂直分布的块，其水平位置相同。
            this.addMidpoint({
                index: newMidpointIndex1,
                edgeID: edgeID,
                left: evtPos.left,
                top: previousPos.top
            })
            this.addMidpoint({
                index: newMidpointIndex2,
                edgeID: edgeID,
                left: evtPos.left,
                top: previousPos.top + 40
            })
        } else {
            this.addMidpoint({
                index: newMidpointIndex1,
                edgeID: edgeID,
                left: evtPos.left - 20,
                top: previousPos.top
            })
            this.addMidpoint({
                index: newMidpointIndex2,
                edgeID: edgeID,
                left: evtPos.left + 20,
                top: previousPos.top
            })
        }
        vue.$nextTick(async () => {
            if (this.plumb == null) {
                throw Error('plumb is null')
            }
            this.plumb.deleteConnection(targetConnection)
            const newMidpoint1UUID = toMidpointUUID(edgeID, newMidpointIndex1)
            const newMidpoint2UUID = toMidpointUUID(edgeID, newMidpointIndex2)

            this.setMidpointPos(newMidpoint1UUID, evtPos.left, previousPos.top)
            this.setMidpointPos(newMidpoint2UUID, evtPos.left, previousPos.top + 40)

            const newEdgeID = await this.resetEdge(edgeID, vue)

            this.fixEdgeConstraints(newEdgeID)

        })

    }
    async straighten(vue: any, conn: Connection) {
        if (this.plumb == null) {
            throw Error("plumb is null")
        }

        // 前驱和后继节点必须都为midpoint才能删除。
        const startElemID: string = (conn.endpoints[0] as any).elementId; // 如果前驱节点是奇数号或者端口，则其为横向；。
        const endElemID: string = (conn.endpoints[1] as any).elementId;
        if (startElemID.startsWith('midpoint-') && endElemID.startsWith('midpoint-')) {
            const connSrcMidpoint = this.queryMidpoint(startElemID)
            const connDestMidpoint = this.queryMidpoint(endElemID)
            const conns = this.getAllConnectionsOnEdge(connDestMidpoint.edgeID)
            for (const i in conns) {
                this.plumb.deleteConnection(conns[i])
            }
            this.removeMidpoint(connSrcMidpoint.edgeID, connSrcMidpoint.index)
            this.removeMidpoint(connDestMidpoint.edgeID, connDestMidpoint.index)
            await vue.$nextTick()
            this.reAllocateMidpointIndex(connDestMidpoint.edgeID)
            const newEdgeID = await this.resetEdge(connDestMidpoint.edgeID, vue)

            this.fixEdgeConstraints(newEdgeID)
        }

    }
    async deleteEdgeByConnection(vue: any, conn: Connection) {
        const edge = this.queryEdgeofConnection(conn)
        await this.deleteEdge(vue, edge)
    }

    // 删除conn所在的边
    async deleteEdge(vue: any, edge: Edge) {
        if (this.plumb == null) {
            throw Error("plumb is null")
        }

        const conns = this.getAllConnectionsOnEdge(edge.id)
        for (const i in conns) {
            this.plumb.deleteConnection(conns[i])
        }
        this.delEdge(edge.id)

        const relatedMidpoints = this.getRelatedMidpoints(edge.id)
        for (const i in relatedMidpoints) {
            this.removeMidpoint(edge.id, relatedMidpoints[i].index)
        }

        await vue.$nextTick()

    }

    async delNode(vue: any, nodeID: string) {
        if (this.plumb == null) {
            throw Error("plumb is null")
        }
        const endpoints = this.plumb.getEndpoints(nodeID)
        for (const ep of endpoints) {
            this.plumb.deleteEndpoint(ep)
        }
        const edges = this.getRelatedEdges(nodeID, "all")
        for (const e of edges) {
            await this.deleteEdge(vue, e)
        }
    }

    //  从  items里面移除节点，删除所有的connection，但是midpoint不动。
    // 接下来，新建一个节点，参数与之前的节点完全相同，连id都是一样的。
    // 最后，重建原先的所有connection。
    async flipNode(vue: any, nodeID: string) {
        if (this.plumb == null) {
            throw Error("plumb is null")
        }

        const node: Node = vue.getNode(nodeID)
        // const eps: Endpoint[] = this.plumb.getEndpoints(nodeID)
        const relatedEdges: Edge[] = this.getRelatedEdges(nodeID, "all")
        const endpoints = this.plumb.getEndpoints(nodeID)
        const detachedConnectors: { srcUUID: string, destUUID: string }[] = []
        for (const ep of endpoints) {
            const connections = this.getConnectionsOnEndpoint((ep as any).getUuid())

            for (const c of connections) {
                const detachedConn = this.getEndpointsUUIDOfConnection(c)
                detachedConnectors.push(detachedConn)
                this.plumb.deleteConnection(c)
            }
            this.plumb.deleteEndpoint(ep)
        }
        node.flip = !node.flip
        await addEndpointsToNode(this.plumb, this, nodeID, node.type, node.flip)
        for (const c of detachedConnectors) {
            const conn = this.plumb.connect({
                // connector: "Straight",
                uuids: [c.srcUUID, c.destUUID],
            });
        }
    }
    // 从jsplumb里面查找，是否endpoint存在。
    endpointExists(endpointUUID: string): boolean {
        if (this.plumb == null) {
            throw Error('plumb is null')
        }
        for (const node of this._vue.items) {
            const endpoints = this.plumb.getEndpoints(node.id)
            for (const endpoint of endpoints) {
                const epUUID = this.getEndpointUUID(endpoint)
                if (epUUID == endpointUUID) {
                    return true
                }
            }
        }
        return false
    }
    getAllEndpoints(): Port[] {
        if (this.plumb == null) {
            throw Error('plumb is null')
        }
        const ports: Port[] = []
        for (const node of this._vue.items) {
            const endpoints = this.plumb.getEndpoints(node.id)
            for (const endpoint of endpoints) {
                const epUUID = this.getEndpointUUID(endpoint)
                const p = parseEndpointID(epUUID)
                ports.push(p)
            }
        }
        return ports
    }



    getNode(nodeID: string): Node {
        const node = this._vue.items.find((node: Node) => node.id == nodeID)
        if (node != null) {
            return node
        } else {
            console.error(this._vue.items)
            throw Error(`Node is null for node id ${nodeID}`)
        }
    }

    endpointHasConnection(endpointUUID: string) {
        for (const edge of this.edges) {
            const edgeSrcUUID = toPortUUID(edge.srcNode, 'output', edge.srcEndpointID)
            const edgeDestUUID = toPortUUID(edge.destNode, 'input', edge.destEndpointID)
            if (edgeSrcUUID == endpointUUID || edgeDestUUID == endpointUUID) {
                return true
            }
        }
        return false
    }

    // 修改端口
    async modifyPorts(nodeID: string, ports: string[], direction: "input" | "output") {
        if (this.plumb == null) {
            throw Error("plumb is null!")
        }
        const node = this.getNode(nodeID)
        const oldPorts = new Set<string>()
        if (direction == "input") {
            node.inputPorts = ports

        }
        if (direction == "output") {
            node.outputPorts = ports
        }
        // 删除同方向上全部的端口，然后进行重新绘制。
        const relatedEdges: Edge[] = this.getRelatedEdges(nodeID, "all")
        const endpoints = this.plumb.getEndpoints(nodeID)
        const detachedConnectors: { srcUUID: string, destUUID: string }[] = []
        for (const ep of endpoints) {
            if (parseEndpointID(this.getEndpointUUID(ep)).direction == direction) {
                const connections = this.getConnectionsOnEndpoint((ep as any).getUuid())

                for (const c of connections) {
                    const detachedConn = this.getEndpointsUUIDOfConnection(c)
                    detachedConnectors.push(detachedConn)
                    this.plumb.deleteConnection(c)
                }
                this.plumb.deleteEndpoint(ep)
            }
        }

        await addEndpointsToNode(this.plumb, this, nodeID, node.type, node.flip)
        // for (const c of detachedConnectors) {
        //     const conn = this.plumb.connect({
        //         // connector: "Straight",
        //         uuids: [c.srcUUID, c.destUUID],
        //     });
        // }
    }
}

